package src;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

import basic.BoxTree;
import basic.CrtBody;
import basic.CrtMesh;
import basic.DoubleXYZ;
import basic.Polygon;
import basic.Vertex;

public class Main {

	public static int NUM_ITER=10000;
	public static int SPACE_SIZE=20;
	public static Date start = null;

	/**
	 * @param args
	 * @return 
	 * @throws IOException 
	 * @throws NumberFormatException 
	 */

	public static void main(String[] args) throws NumberFormatException, IOException {
		double a=10;
		double b=2;
		final int n1 = 50;
		final int n2 = 50;

		DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss,SSS");
		int nruns = 50;
		boolean usetriangle = false;
		int timebuildtorus = 0;
		int timebuildtree = 0;
		int timecs = 0;

		for (int w = 0; w < nruns; w++) {
			// System.out.print("Loading a torus with a major radius of "+ a + " and a minor radius of "+b+ " composed of "+ n1*n2+" polygons...");

			start = new Date();
			int uc, vc;
			ArrayList<Vertex> vertices = new ArrayList<Vertex>();
			CrtMesh meshes = new CrtMesh("torus");
			for(uc=0; uc<n1; uc++){
				for(vc=0; vc<n2; vc++) {
					double u1 = (2.0* Math.PI *uc) / n1; 
					double u2 = (2.0*Math.PI*(uc+1)) / n1; 
					double v1 = (2.0*Math.PI*vc) / n2; 
					double v2 = (2.0*Math.PI*(vc+1)) / n2;

					double[] p1 = new double[3];
					double[] p2 = new double[3];
					double[] p3 = new double[3];
					double[] p4 = new double[3];

					p1[0] = (a - b * Math.cos(v1)) * Math.cos(u1);
					p2[0] = (a - b * Math.cos(v1)) * Math.cos(u2);
					p3[0] = (a - b * Math.cos(v2)) * Math.cos(u1);
					p4[0] = (a - b * Math.cos(v2)) * Math.cos(u2);
					p1[1] = (a - b * Math.cos(v1)) * Math.sin(u1);
					p2[1] = (a - b * Math.cos(v1)) * Math.sin(u2);
					p3[1] = (a - b * Math.cos(v2)) * Math.sin(u1);
					p4[1] = (a - b * Math.cos(v2)) * Math.sin(u2);
					p1[2] = b * Math.sin(v1);
					p2[2] = b * Math.sin(v1);
					p3[2] = b * Math.sin(v2);
					p4[2] = b * Math.sin(v2);

					if (!usetriangle) {
						vertices.add(new Vertex(p1[0], p1[1], p1[2]));
						vertices.add(new Vertex(p2[0], p2[1], p2[2]));
						vertices.add(new Vertex(p4[0], p4[1], p4[2]));
						vertices.add(new Vertex(p3[0], p3[1], p3[2]));
						meshes.addMeshPrimitive(new Polygon(vertices));
						vertices.clear();
					}
					else {
						vertices.add(new Vertex(p1[0], p1[1], p1[2]));
						vertices.add(new Vertex(p2[0], p2[1], p2[2]));
						vertices.add(new Vertex(p3[0], p3[1], p3[2]));
						meshes.addMeshPrimitive(new Polygon(vertices));
						vertices.clear();

						vertices.add(new Vertex(p4[0], p4[1], p4[2]));
						vertices.add(new Vertex(p2[0], p2[1], p2[2]));
						vertices.add(new Vertex(p3[0], p3[1], p3[2]));
						meshes.addMeshPrimitive(new Polygon(vertices));
						vertices.clear();
					}
				}
			}
			// System.out.println("done");
			timebuildtorus += getMs();
			// System.out.print("Building hierarchy...");

			start = new Date();
			BoxTree hierc = new BoxTree(meshes);
			timebuildtree  += getMs();
			CrtBody torus1= new CrtBody("torus1",hierc);
			CrtBody torus2= new CrtBody("torus2",hierc);
			// System.out.println("done");

			int col_count = 0;


			// System.out.print("Running "+ NUM_ITER + " tests with two tori at random placements  in a space of size "+ SPACE_SIZE+ " ...");  

			for (int i = 0; i != NUM_ITER; ++i) {
				double pos1x = Basic.rnd()*SPACE_SIZE;
				double pos1y = Basic.rnd()*SPACE_SIZE;
				double pos1z = Basic.rnd()*SPACE_SIZE;
				double pos2x = Basic.rnd()*SPACE_SIZE;
				double pos2y = Basic.rnd()*SPACE_SIZE;
				double pos2z = Basic.rnd()*SPACE_SIZE;
				Quaternion q = Basic.random();
				Quaternion q2 = Basic.random();

				start = new Date();
				torus1.setPosition(new DoubleXYZ(pos1x,pos1y,pos1z));
				torus1.setOrientation(q);
				torus2.setPosition(new DoubleXYZ(pos2x,pos2y,pos2z));
				torus2.setOrientation(q2);
				if(test(torus2,torus1)) ++col_count;
				timecs += getMs();
			}

			/*
			String nomefile = "ptquat"+(w+1)+".txt";
			FileInputStream fstream = new FileInputStream(nomefile);
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			while ((strLine = br.readLine()) != null)   {
				String[] columnDetail = strLine.split("\\t");
				double[] doubs = new double[columnDetail.length];
				for (int j = 0; j < columnDetail.length; j++) doubs[j] = Double.parseDouble(columnDetail[j]);

				start = new Date();
				torus1.setPosition(new DoubleXYZ(doubs[0],doubs[1],doubs[2]));
				torus1.setOrientation(new Quaternion(doubs[3],doubs[4],doubs[5],doubs[6]));
				torus2.setPosition(new DoubleXYZ(doubs[7],doubs[8],doubs[9]));
				torus2.setOrientation(new Quaternion(doubs[10],doubs[11],doubs[12],doubs[13]));
				if(test(torus2,torus1)) ++col_count;
				timecs += getMs();

			}
			in.close();
			 */

			// System.out.println("done");
			// System.out.println("Number of collisions: "+ col_count );
			System.out.println("Number of collisions: "+ col_count );
			System.out.println("Iterazione "+(w+1));
		}

		System.out.println("ELENCO TEMPI (basati su "+nruns+" iterazioni)");
		System.out.println("Creazione torus (ms): "+ ((double)timebuildtorus/(double)nruns));
		System.out.println("Creazione gerarchia (ms): "+ ((double)timebuildtree/(double)nruns));
		System.out.println("Singola collision checking (ms): "+ (((double)timecs/(double)(nruns*NUM_ITER))));
		System.out.println("Tutte le "+(nruns*NUM_ITER)+" collision checking (ms): "+ timecs);
	}

	private static boolean test(CrtBody obj1, CrtBody obj2)
	{
		return CrtBody.intersect(obj1,obj2);
	}

	/**
	 * Metodo per la generazione dei millisecondi
	 * @return
	 */
	public static long getMs() {
		return (new Date().getTime() - start.getTime());
	}
}
